

#import "UPath.h"
#import "PSWUPath.h"

@interface UPath(PrivateMethods)
 
- growParams;
- growOps;

@end

@implementation UPath

//************************************************************************
// creating and destroying

- initCountParams:(int)numParams countOps:(int)numOps
{  
   [super init];
   maxParams = numParams;
   maxOps = numOps;
   bboxParams = (float *)NXZoneMalloc([self zone],sizeof(float) * (maxParams + 4));
   bbox = bboxParams;
   params = &bboxParams[4];
   bboxOps = (char *)NXZoneMalloc([self zone],maxOps + 2);
   ops = &bboxOps[2];
   ping = NO;
   [self resetFill];
   return self;
}
   
- init
{  
   [self initCountParams:32 countOps:16];
   return self;
}

- free
{
   NX_FREE(bboxParams);
   NX_FREE(bboxOps);
   return [super free];
}

- copyFromZone:(NXZone *)zone
{
   UPath *theCopy;

   theCopy = [super copyFromZone:zone];
   theCopy->bboxParams = NXZoneMalloc(zone,sizeof(float) * (maxParams + 4));
   theCopy->bboxOps = NXZoneMalloc(zone,maxOps + 2);
   bcopy(bboxParams,theCopy->bboxParams,(sizeParams + 4) * (sizeof(float)/sizeof(char)));
   bcopy(bboxOps,theCopy->bboxOps,sizeOps + 2);
   theCopy->bbox = theCopy->bboxParams;
   theCopy->params = theCopy->bboxParams + 4 * sizeof(float);
   theCopy->ops = theCopy->bboxOps + 2; 
   return theCopy;
}

//************************************************************************
// accessing

- getBounds:(NXRect *)aRect
{
   NXSetRect(aRect,bbox[0],bbox[1],bbox[2] - bbox[0],bbox[3] - bbox[1]);
   return self;
}

//************************************************************************
// defining user path

- resetFill
{
   sizeParams = sizeOps = 0;
   cp.x = cp.y = 0.0;
   bbox[0] = bbox[1] = 1.0e6;
   bbox[2] = bbox[3] = -1.0e6;
   bboxOps[0] = dps_ucache;
   bboxOps[1] = dps_setbbox;
   return self;
}

- updateBBoxOnX:(float)x andY:(float)y
{
   if(x < bbox[0])
	bbox[0] = x;
   if(y < bbox[1])
	bbox[1] = y;
   if(x > bbox[2])
	bbox[2] = x;
   if(y > bbox[3])
	bbox[3] = y;
   return self;
}

- moveto:(float)x :(float)y
{
    if(sizeParams + 2 > maxParams)
        if(![self growParams])
            return nil;
    if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;
    params[sizeParams++] = x;
    params[sizeParams++] = y;
    ops[sizeOps++] = dps_moveto;
    [self updateBBoxOnX:x andY:y];
    cp.x = x;
    cp.y = y;    
    return self;
}

- rmoveto:(float)x :(float)y
{
   if(sizeParams + 2 > maxParams)
        if(![self growParams])
            return nil;
   if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;    
   ops[sizeOps++] = dps_rmoveto;
   params[sizeParams++] = x;
   params[sizeParams++] = y;
   cp.x += x;
   cp.y += y;
   [self updateBBoxOnX:cp.x andY:cp.y]; 
   return self;
}  

- lineto:(float)x :(float)y
{
    if(sizeParams + 2 > maxParams)
        if(![self growParams])
            return nil;
    if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;
    ops[sizeOps++] = dps_lineto;
    params[sizeParams++] = x;
    params[sizeParams++] = y;
    [self updateBBoxOnX:x andY:y];
    cp.x = x;
    cp.y = y;    
    return self;
}

- rlineto:(float)x :(float)y
{
   if(sizeParams + 2 > maxParams)
        if(![self growParams])
            return nil;
   if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;    
   ops[sizeOps++] = dps_rlineto;
   params[sizeParams++] = x;
   params[sizeParams++] = y;
   cp.x += x;
   cp.y += y;
   [self updateBBoxOnX:cp.x andY:cp.y]; 
   return self;
}  

- curveto:(float)x1 :(float)y1 :(float)x2 :(float)y2 :(float)x3 :(float)y3
{
   if(sizeParams + 6 > maxParams)
        if(![self growParams])
            return nil;
   if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;
   ops[sizeOps++] = dps_curveto;
   params[sizeParams++] = x1;
   params[sizeParams++] = y1;
   [self updateBBoxOnX:x1 andY:y1];
   params[sizeParams++] = x2;
   params[sizeParams++] = y2;
   [self updateBBoxOnX:x2 andY:y2];
   params[sizeParams++] = x3;
   params[sizeParams++] = y3;
   [self updateBBoxOnX:x3 andY:y3];
   cp.x = x3;
   cp.y = y3;    
   return self;
}

- rcurveto:(float)dx1 :(float)dy1 :(float)dx2 :(float)dy2 :(float)dx3 :(float)dy3
{
   if(sizeParams + 6 > maxParams)
        if(![self growParams])
            return nil;
   if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;    
   ops[sizeOps++] = dps_rcurveto;
   params[sizeParams++] = dx1;
   params[sizeParams++] = dy1;
   params[sizeParams++] = dx2;
   params[sizeParams++] = dy2;
   params[sizeParams++] = dx3;
   params[sizeParams++] = dy3;
   [self updateBBoxOnX:cp.x + dx1 andY:cp.y + dx1];
   [self updateBBoxOnX:cp.x + dx2 andY:cp.y + dx2];
   [self updateBBoxOnX:cp.x + dx3 andY:cp.y + dx3];
   cp.x += dx3;
   cp.y += dy3; 
   return self;
}  

- arc:(float)x :(float)y :(float)r :(float)ang1 :(float)ang2
{
   if(sizeParams + 5 > maxParams)
        if(![self growParams])
            return nil;
   if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;
   ops[sizeOps] = dps_arc;
   sizeOps = sizeOps + 1;
   params[sizeParams++] = x;
   params[sizeParams++] = y;
   params[sizeParams++] = r;
   params[sizeParams++] = ang1;
   params[sizeParams++] = ang2;
   [self updateBBoxOnX:x + r andY:y + r];
   [self updateBBoxOnX:x - r andY:y - r];   
   cp.x = x + cos(ang2 / 57.3) * r;
   cp.y = y + sin(ang2 / 57.3) * r;    
   return self;
}

- arcn:(float)x :(float)y :(float)r :(float)ang1 :(float)ang2
{
   if(sizeParams + 5 > maxParams)
        if(![self growParams])
            return nil;
   if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;
   ops[sizeOps++] = dps_arcn;
   params[sizeParams++] = x;
   params[sizeParams++] = y;
   params[sizeParams++] = r;
   params[sizeParams++] = ang1;
   params[sizeParams++] = ang2;
   [self updateBBoxOnX:x + r andY:y + r];
   [self updateBBoxOnX:x - r andY:y - r];   
   cp.x = x + cos(ang2 / 57.3) * r;
   cp.y = y + sin(ang2 / 57.3) * r;    
   return self;
}

- arct:(float)x1 :(float)y1 :(float)x2 :(float)y2 :(float)r
{
   if(sizeParams + 5 > maxParams)
        if(![self growParams])
            return nil;
   if(sizeOps + 1 > maxOps)
        if(![self growOps])
            return nil;
   ops[sizeOps++] = dps_arct;
   params[sizeParams++] = x1;
   params[sizeParams++] = y1;
   params[sizeParams++] = x2;
   params[sizeParams++] = y2;
   params[sizeParams++] = r;
   [self updateBBoxOnX:x1 andY:y1];
   [self updateBBoxOnX:x2 andY:y2];   
   cp.x = x2;
   cp.y = y2;    
   return self;
}

- closepath
{
   ops[sizeOps++] = dps_closepath; 
   return self;
}


//************************************************************************
// sending 

- debugSend:(int)op cached:(BOOL)cache
{
   ping = YES;
   return [self send:op cached:cache];
}

- send:(int)op cached:(BOOL)cache
{
   NXHandler exception;

   exception.code = 0;
      NX_DURING
        if(cache)
	    DPSDoUserPath(params, sizeParams, dps_float, bboxOps,
		      sizeOps + 2, bbox, op);
        else
            DPSDoUserPath(params, sizeParams, dps_float, bboxOps + 1,
		      sizeOps + 1, bbox, op);
	if(ping)
	    NXPing();	
      NX_HANDLER
	exception = NXLocalHandler;
      NX_ENDHANDLER
	if(exception.code){
	    NXReportError(&exception);
	    return nil;
	} else 
	    return self;
}

//************************************************************************
// archiving

- write:(NXTypedStream *)stream
{
   [super write:stream];
   NXWriteTypes(stream,"iiii",&sizeParams,&sizeOps,&maxParams,&maxOps);
   NXWritePoint(stream,&cp);
   NXWriteArray(stream,"f",sizeParams + 4,bboxParams);
   NXWriteArray(stream,"c",sizeOps + 2,bboxOps);
   return self;
}

- read:(NXTypedStream *)stream
{
   [super read:stream];
   NXReadTypes(stream,"iiii",&sizeParams,&sizeOps,&maxParams,&maxOps);
   NXReadPoint(stream,&cp);
   bboxParams = NXZoneMalloc([self zone],sizeof(float) * (maxParams + 4));
   bboxOps = NXZoneMalloc([self zone],maxOps + 2);
   NXReadArray(stream,"f",sizeParams + 4,bboxParams);
   NXReadArray(stream,"c",sizeOps + 2,bboxOps);
   return self;
}

- awake
{
   [super awake];
   ping = NO;
   bbox = bboxParams;
   params = &bboxParams[4];
   ops = &bboxOps[2];
   return self;
}
              
@end

@implementation UPath(PrivateMethods)

- growParams
{
   	maxParams *= 2;
	bbox = bboxParams = (float *)NXZoneRealloc([self zone], bboxParams,sizeof(float) * (maxParams + 4));
	params = &bboxParams[4]; 
   	return self;
}

- growOps
{
   	maxOps *= 2;
	bboxOps = (char *)NXZoneRealloc([self zone], bboxOps,maxOps + 2);
	ops = &bboxOps[2];
   	return self;
}

@end
